#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "compare.h"

/* STANDARD FREQUENCIES (Hz) OF MUSICAL NOTES FOR INSTRUMENTS EXCEPT PIANO
 *  OCT
 * +---+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
 * | # |    C   |    C#  |   D    |    D#  |    E   |   F    |   F#   |   G    |  G#    |   A    |  A#    |   B    |
 * | 1 | 32.703 | 34.648 | 36.708 | 38.891 | 41.203 | 43.654 | 46.249 | 48.999 | 51.913 | 55.000 | 58.270 | 61.735 | 
 * | 2 | 65.406 | 69.296 | 73.416 | 77.782 | 82.407 | 87.307 | 92.499 | 97.999 | 103.83 | 110.00 | 116.54 | 123.47 | 
 * | 3 | 130.81 | 138.59 | 146.83 | 155.56 | 164.81 | 174.61 | 185.00 | 196.00 | 207.65 | 220.00 | 233.08 | 246.94 | 
 * | 4 | 261.63 | 277.18 | 293.66 | 311.13 | 329.63 | 349.23 | 369.99 | 392.00 | 415.30 | 440.00 | 466.16 | 493.88 | 
 * | 5 | 523.25 | 554.37 | 587.33 | 622.25 | 659.26 | 698.46 | 739.99 | 783.99 | 830.61 | 880.00 | 932.33 | 987.77 | 
 * | 6 | 1046.5 | 1108.7 | 1174.7 | 1244.5 | 1318.5 | 1396.9 | 1480.0 | 1568.0 | 1661.2 | 1760.0 | 1864.7 | 1975.5 | 
 * | 7 | 2093.0 | 2217.5 | 2349.3 | 2489.0 | 2637.0 | 2793.8 | 2960.0 | 3136.0 | 3322.4 | 3520.0 | 3729.3 | 3951.1 | 
 * | 8 | 4186.0 | 4434.9 | 4698.6 | 4978.0 | 5274.0 | 5587.7 | 5919.9 | 6271.9 | 6644.9 | 7040.0 | 7458.6 | 7902.1 | 
 * +---+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
 */

double _note_freq[] = {
  16.352, 17.324, 18.354, 19.446, 20.602, 21.827, 23.125, 24.500, 25.957, 27.500, 29.135, 30.866,
  32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55.000, 58.270, 61.735,
  65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.83, 110.00, 116.54, 123.47,
  130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94,
  261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
  523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77,
  1046.5, 1108.7, 1174.7, 1244.5, 1318.5, 1396.9, 1480.0, 1568.0, 1661.2, 1760.0, 1864.7, 1975.5,
  2093.0, 2217.5, 2349.3, 2489.0, 2637.0, 2793.8, 2960.0, 3136.0, 3322.4, 3520.0, 3729.3, 3951.1,
  4186.0, 4434.9, 4698.6, 4978.0, 5274.0, 5587.7, 5919.9, 6271.9, 6644.9, 7040.0, 7458.6, 7902.1,
  8372.0, 8869.8, 9397.2, 9956.0, 10548., 11175., 11840., 12544., 13290., 14080., 14917., 15804.
};

char _note_name[][3] = {
  "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};

char compare_detect(double freq, CompareInfo* info) {
  unsigned char a,b,m;
  // fastly detects conforming with note
  for (a=0,b=119,m=b/2; (b-a)>1; a=freq<_note_freq[m]?a:m, b=freq<_note_freq[m]?m:b, m=(a+b)/2);
  // correct middle note
  if(m>0   && fabs(freq-_note_freq[m])>fabs(freq-_note_freq[m-1])) m--;
  if(m<119 && fabs(freq-_note_freq[m])>fabs(freq-_note_freq[m+1])) m++;
  // fills result of comparing
  info->prev.freq = m>0?_note_freq[m-1]:_note_freq[m+11]/2.0;   info->prev.octa = (m-1)/12; info->prev.name = _note_name[(m>0?m-1:m+11) % 12];
  info->curr.freq = _note_freq[m];                              info->curr.octa = m / 12;   info->curr.name = _note_name[m % 12];
  info->next.freq = m<119?_note_freq[m+1]:_note_freq[m-11]*2.0; info->next.octa = (m+1)/12; info->next.name = _note_name[(m<119?m+1:m-11) % 12];
  // detects out of band cases
  if (freq<_note_freq[0]) return -1;
  if (freq>_note_freq[119]) return +1;
  return 0;
}

#if 0

main(){
  unsigned int i;
  CompareInfo c;
#define TEST(f) {							\
    compare_detect(f, &c);							\
    printf("F: %f P-F: %f P-N: %s P-O: %d C-F: %f C-N: %s C-O: %d N-F: %f N-N: %s N-O: %d\n", f, \
	   c.prev.freq, c.prev.name, c.prev.octave,			\
	   c.curr.freq, c.curr.name, c.curr.octave,			\
	   c.next.freq, c.next.name, c.next.octave);			\
  }
#if 0
  TEST(18.500);
  TEST(19.500);
  TEST(16.200);
  TEST(15804.2);
  TEST(2715.4);
  TEST(14047.869250);
#else
  for (i=0; i<10000000; i++) {
    double r = ((double)rand() / ((double)(RAND_MAX)+(double)(1)));
    double f = _note_freq[0]+r*(_note_freq[119]-_note_freq[0]);
    comp_find(f, &c);
    if(!((fabs(c.curr.freq - f) < fabs(c.prev.freq - f)) && (fabs(c.curr.freq - f) < fabs(c.next.freq - f))))
      printf("ERROR: f=%f\n", f);
  }
#endif
}

#endif

/*
CompData comp_execute(double freq) {
  unsigned char o;
  unsigned char n;
  CompData r;
  
  for (o=1; o<9; o++) {
    if (_note_freq[o*12] <= freq && freq < _note_freq[(o+1)*12]) {
      for (n=0; n<12; n++) {
	if (_note_freq[o*12+n] <= freq && freq < _note_freq[o*12+n+1]) {
	  if (freq - _note_freq[o*12+n] < _note_freq[o*12+n+1] - freq) {
	    r.prev.freq = _note_freq[o*12+n-1];    r.prev.octave = n>0 ? o : o-1;    r.prev.name = _note_name[n>0 ? n-1 : 11];
	    r.curr.freq = _note_freq[o*12+n];      r.curr.octave = o;                r.curr.name = _note_name[n];
	    r.next.freq = _note_freq[o*12+n+1];    r.next.octave = n<11 ? o : o+1;   r.next.name = _note_name[n<11 ? n : 0];
	  } else {
	    r.prev.freq = _note_freq[o*12+n];      r.prev.octave = o;                r.prev.name = _note_name[n];
	    r.curr.freq = _note_freq[o*12+n+1];    r.curr.octave = n<11 ? o : o+1;   r.curr.name = _note_name[n<11 ? n : 0];
	    r.next.freq = _note_freq[o*12+n-1];    r.next.octave = n>0 ? o : o-1;    r.next.name = _note_name[n>0 ? n-1 : 11];
	  }
	  return r;
	}
      }
    }
  }
}
*/
